#include <stdio.h>
#include <stdlib.h>
#include "ihex.h"

void ihex_free(struct s_ihex_node * node)
{
  struct s_ihex_node * tmp;  
  while(node)
  {
    tmp = node;
    if(tmp->data) free(tmp->data);
    node = tmp->next;
    free(tmp);
  }
}

struct s_ihex_node * ihex_load(char*name)
{
  FILE          *file;
  unsigned char line[256];
  unsigned char temp[256];
  unsigned int  lp,error,done,i,j,len,current_address;
  unsigned char command;
  struct s_ihex_node *node,*root_node;
  
  file=fopen(name,"rb");
  
  if(!file)
  {
    printf("ihex:error: unable to open file %s !\n",name);
    exit(-1);
  }
 
  node  = root_node = 0;
  error = done = 0;
 
 
  while(!error)
  {
    lp = 0;
    /* read line */
    fgets(line,256,file);
    /* check leading char */
    if(line[lp++]!=':')
    {
       error=1;
       break;
    }
    
    /* get line len */
    for(i=0;i<2;i++) temp[i] = line[lp++];
    temp[i] = 0;
    len     = strtoul(temp,(char**)NULL,16);

    /* get address lsb */
    for(i=0;i<4;i++) temp[i] = line[lp++];
    temp[i] = 0;
    current_address = (current_address&0xffff0000) | (strtoul(temp,(char**)NULL,16)&0xffff);
 
    /* get command */
    for(i=0;i<2;i++) temp[i] = line[lp++];
    temp[i] = 0;
    command = strtoul(temp,(char**)NULL,16);
    
    switch(command)
    {
      /* get address msb */
      case 0x04:
        
        for(i=0;i<4;i++) temp[i] = line[lp++];
        temp[i] = 0;
        current_address = current_address&0xffff | (strtoul(temp,(char**)NULL,16)<<16);
        break;
    
      /* get data */
      case 0x00:
        
        /* extract line data */
        for(i=0;i<len;i++)
        {
          temp[0] = line[lp++];
          temp[1] = line[lp++];
          temp[2] = 0;
      
          /* check if the current address is with the page address */
          if(node==0)
          {
            /* create list */
            node = malloc(sizeof(struct s_ihex_node));           
            root_node = node;
            node->address = (current_address+i)&0xfffffe00;                    
            node->data    = malloc(512*sizeof(unsigned char));   
            for(j=0;j<512;j++) node->data[j]=0xff;
            node->next    = 0;                                   
          }
          else if(((current_address+i)&0xfffffe00)!=(node->address))
          {
            /* address in not within the current page */
            node->next    = malloc(sizeof(struct s_ihex_node));           
            node=node->next;
            node->address = (current_address+i)&0xfffffe00;                    
            node->data    = malloc(512*sizeof(unsigned char));   
            for(j=0;j<512;j++) node->data[j]=0xff;
            node->next    = 0;                                   
          }
          node->data[current_address - (node->address) + i] = strtoul(temp,(char**)NULL,16);
        }
        
        /* skip checksum */
        break;
        
      default:
      
        done=1;
        break;    
    } 
  
    if(error || done) break;
  }
  fclose(file);
  
  if(!error)
    return root_node;
  else
    return 0;
  
}
